#include "StdAfx.h"
#include "KCurlObject.h"
//#include <atlbase.h>
//#include <atlconv.h>
#include "winapi.h"

KCurlObject::KCurlObject()
	: ulHandle(NULL),
	  m_pIBlockTask(NULL),
	  m_pIOutHttpHead(NULL)
{
	m_uHttpStatusCode = 0;

	this->ulHandle = curl_easy_init();
	this->headers = NULL;
}

KCurlObject::~KCurlObject()
{
	if (this->ulHandle)
	{
		curl_easy_cleanup(this->ulHandle);
		ulHandle = 0;
	}
	if (this->headers)
	{
		curl_slist_free_all(headers);
	}
}

CURL* KCurlObject::GetCurObject()
{
	return ulHandle;
}


//////////////////////////////////////////////////////////////////////////
// Curlªÿµ˜°£
// Curlœ¬‘ÿΩ¯∂»ªÿµ˜£¨dltotal/dlnow «œ¬‘ÿΩ¯∂»£ªultotal/ulnow «…œ¥´Ω¯∂»°£
int KCurlObject::_download_progress_callback(IN void *clientp,
											 IN double dltotal,
											 IN double dlnow,
											 IN double ultotal,
											 IN double ulnow)
{
	// ∑µªÿ0±Ì æºÃ–¯°¢1±Ì æ÷–∂œÕÀ≥ˆ°£
	BOOL bBreak = FALSE;
	
	KCurlObject* _pThis = (KCurlObject*)clientp;
	if (_pThis)
		_pThis->OnCurlProgress(dltotal, dlnow, bBreak);
	//_tprintf(_T("_download_progress_callback --%f |%d\n"), dlnow, ::GetCurrentThreadId());

	return bBreak;
}

// Curl–¥httpÕ∑ªÿµ˜°£
size_t KCurlObject::_curl_writehead_callback(IN char * buffer, //data that get back
									         IN size_t nSize, 
											 IN size_t nCount, 
											 IN void * pOutStream)//our own mem in CURLOPT_WRITEDATA
{
	size_t nDataSize = nSize * nCount;

	KCurlObject* _pThis = (KCurlObject*)pOutStream;
	if (_pThis)
	{
		_pThis->ObtainHttpStatusCode(buffer);

		if (!_pThis->OnCurlWirteHead((c_byte*)buffer, nDataSize))
			nDataSize -= 1;
	}
	//_tprintf(_T("_curl_writehead_callback |%d\n"), GetCurrentThreadId());
	return nDataSize;
}

// Curl–¥ ˝æ›ªÿµ˜°£
size_t KCurlObject::_curl_writedata_callback(IN char * buffer, 
											 IN size_t nSize, 
											 IN size_t nCount, 
											 IN void * pOutStream)
{
	size_t nDataSize = nSize * nCount;

	KCurlObject* _pThis = (KCurlObject*)pOutStream;
	if (_pThis)
		if (!_pThis->OnCurlWriteData((c_byte*)buffer, nDataSize, NULL))
			nDataSize -= 1;
	//_tprintf(_T("_curl_writedata_callback:%d |%d (%x)\n"), nDataSize, GetCurrentThreadId(), _pThis);
	return nDataSize;
}

//////////////////////////////////////////////////////////////////////////
// œÏ”¶Curlªÿµ˜∫Ø ˝°£
// œÏ”¶HttpÕ∑£¨∑µªÿFASLE£¨±Ì æ«Î«ÛÕÀ≥ˆ°£
BOOL KCurlObject::OnCurlWirteHead(IN c_byte * pbyBuf, //data that get back
								  IN u_int uSize)
{
	//printf(("HttpHead->%s\n"), pbyBuf);

	if (m_pIOutHttpHead)
		m_pIOutHttpHead->Write(pbyBuf, uSize, NULL);

	return TRUE;
}

// œÏ”¶–¥ ˝æ›£¨∑µªÿFASLE£¨±Ì æ«Î«ÛÕÀ≥ˆ°£
BOOL KCurlObject::OnCurlWriteData(IN c_byte * pbyBuf, 
								  IN u_int uSize, 
								  OUT u_int * pWrittenSize)
{
	HRESULT hResult = E_FAIL;

	if (m_pIBlockTask)
		hResult = m_pIBlockTask->Write(pbyBuf, uSize, pWrittenSize);

	return SUCCEEDED(hResult);
}

// œÏ”¶œ¬‘ÿΩ¯∂»£¨∑µªÿFASLE£¨±Ì æ«Î«ÛÕÀ≥ˆ°£
void KCurlObject::OnCurlProgress(IN double dltotal,
								 IN double dlnow,
								 OUT BOOL& bBreak)
{

	if (m_pIBlockTask)
		m_pIBlockTask->OnBlockProgress(dltotal, dlnow, bBreak);

}

//////////////////////////////////////////////////////////////////////////
// ≈‰÷√Curl∂‘œÛ°£
/*
@ FuncName	: SetCurlObject
@ Brief		: µ˜÷√Curl∂‘œÛ£¨∑µªÿCurl¥ÌŒÛ¬Î°£
*/
u_int KCurlObject::SetCurlObject(IN LPCTSTR pszURL,
								 IN IBlockTask* _pIBlockTask)
{
	CURLcode retval;

	unsigned long ulResult = (unsigned long)-1;

	this->m_pIBlockTask = _pIBlockTask;
	this->m_uHttpStatusCode	= 0;
	//÷ß≥÷∑˛ŒÒ∆˜—πÀı
	/*if (bIsSupportZIP)
	{
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_ENCODING, "gzip, deflate");
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		} 
	}*/
	//Modify End

	// ≈‰÷√Url
	// 注意！！！
	//strUrl = ATL::CW2A(pszURL);
	retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_URL, strUrl.c_str());
	if (retval != CURLE_OK)
	{
		ulResult = (unsigned long)retval;
		goto Exit0;
	} 

	// ≈‰÷√httpÕ∑°£
	headers = curl_slist_append (headers, "Connection: close");
	if (headers)
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_HTTPHEADER, headers);

	// ≈‰÷√–¥httpÕ∑ªÿµ˜£°
	{
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_HEADERFUNCTION, _curl_writehead_callback);
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}

		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_WRITEHEADER, (void*)this);
		if (retval != CURLE_OK)
		{
			ulResult = -1;
			goto Exit0;
		}
	}

	// ≈‰÷√–¥Œƒº˛ªÿµ˜£°
	{
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_WRITEFUNCTION, &KCurlObject::_curl_writedata_callback);
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}

		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_WRITEDATA, (void*)this);
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}
	}
	// ≈‰÷√Ω¯∂»ªÿµ˜°£
	{
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_NOPROGRESS, 0); 
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}

		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_PROGRESSFUNCTION,
			_download_progress_callback);
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}

		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_PROGRESSDATA, (void*)this); 
		if (retval != CURLE_OK)
		{
			ulResult = (unsigned long)retval;
			goto Exit0;
		}
	}

	{
		u_int uStartPos = m_pIBlockTask->GetStartPos();
		u_int uEndPos = m_pIBlockTask->GetEndPos();
		// ≤ªƒ‹”√¡Ÿ ±±‰¡ø°£
		if (-1 == uEndPos || 0 == uEndPos)
			sprintf(m_szRange, "%d-", uStartPos);
		else
			sprintf(m_szRange, "%d-%d", uStartPos, uEndPos);
		curl_easy_setopt((CURL*)ulHandle, CURLOPT_RANGE, &m_szRange);
	}

	curl_easy_setopt((CURL*)ulHandle, CURLOPT_FILETIME, 1); //ignore this error if encounter!

	//retval = curl_easy_perform((CURL*)ulHandle);
	//if (retval != CURLE_OK)
	//{
	//	//if (XGetLastError() == KCURL_BLOCK_TIMEOUT) 
	//	//	ulResult = KCURL_BLOCK_TIMEOUT;
	//	//else
	//	//	ulResult = (unsigned long)retval;
	//	//// ¥˙±Ì”√ªß»°œ˚¡Àœ¬‘ÿ
	//	//if (80  == retval)
	//	//{
	//	//	DeleteFile(strFilePath);
	//	//}
	//	goto Exit0;
	//}

	//if (nHeadInfo == 404)
	//{
	//	//ulResult = KCURL_NOT_FOUND_404;
	//	goto Exit0;
	//}

	// Curl÷–”–»°∆Ωæ˘¥¯øÌµƒ¥˙¬Î°£
	//nEndTickCount = ::GetTickCount();
	//{
	//	DWORD nTime = nEndTickCount - nStartTickCount;
	//	/*		if (fseek(stRSResumeParam.fp, 0, SEEK_END) == 0) //move the file pointer to the end of the file.
	//	{	//success
	//	long curFilePtr = ftell(stRSResumeParam.fp);
	//	if (curFilePtr >= 1 && nEndTickCount - nStartTickCount > 0)
	//	theApp.PutBandWidth(curFilePtr * 1000 / (nEndTickCount - nStartTickCount));
	//	}
	//	*/	
	//}

	ulResult = 0;
Exit0:
	//if (stRSResumeParam.fp)
	//{
	//	fclose(stRSResumeParam.fp);
	//	stRSResumeParam.fp = NULL;

	//	//set the file last-modification time
	//	long lmt = 0;
	//	if (curl_easy_getinfo((CURL*)ulHandle, CURLINFO_FILETIME, &lmt) == CURLE_OK)
	//	{
	//		struct utimbuf ub;

	//		lmt += (4*60*60); // add 4 hours - convert UTC time to local time

	//		ub.actime = lmt;
	//		ub.modtime = lmt;

	//		utime(pcszSaveAs, &ub);
	//	}
	//}

	return ulResult;
}

u_int KCurlObject::SetTimeOut(u_long ulConnectInSeconds,
							  u_long ulDwonloadInSeconds)
{
	CURLcode retval = CURLE_OK;

	retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_CONNECTTIMEOUT, ulConnectInSeconds);
	if (retval != CURLE_OK)
	{
		return (u_int)retval;
	} 

	retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_TIMEOUT, ulDwonloadInSeconds);
	if (retval != CURLE_OK)
	{
		return (u_int)retval;
	} 

	return retval;
}

void KCurlObject::AddHttpHead(LPCSTR pszHttpHead)
{
	headers = curl_slist_append (headers, pszHttpHead);
}

/*
@ FuncName	: SetHeadHandler
@ Brief		: µ˜÷√httpÕ∑¥¶¿Ì∆˜°£
*/
void KCurlObject::SetHeadHandler(IKWrite * pIKWrite)
{
	m_pIOutHttpHead = pIKWrite;
}

/*
@ FuncName	: SetProxy
@ Brief		: …Ë÷√¥˙¿Ì°£
*/
u_int KCurlObject::SetProxy(IN int nProxyType, 
						   IN LPCTSTR pszServerAndPort,
						   IN LPCTSTR pszUserName,
						   IN LPCTSTR pszPassword)
{
	CURLcode retval = CURLE_OK;
	curl_proxytype cpt = CURLPROXY_HTTP;
	// http¥˙¿Ì°£
	if (1 == nProxyType)
		cpt = CURLPROXY_HTTP;
	// SOCKS5¥˙¿Ì°£
	else if (5 == nProxyType)
		cpt = CURLPROXY_SOCKS5;

	retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_PROXYTYPE, cpt);
	if (retval != CURLE_OK)
	{
		return (unsigned long)retval;
	}

	if (IsStringValid(pszServerAndPort))
	{
		// 注意
		//strServerAndPort = ATL::CW2A(pszServerAndPort);
		retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_PROXY, strServerAndPort.c_str());
		if (retval != CURLE_OK)
		{
			return (unsigned long)retval;
		} 
	}

	if (IsStringValid(pszUserName))
	{
		std::string strPasswordA;
		// 注意
		//strUserAndPwd = ATL::CW2A(pszUserName);

		//if (IsStringValid(pszPassword))
//			strPasswordA = ATL::CW2A(pszPassword);
 
		strUserAndPwd.append(":");
		strUserAndPwd.append(strPasswordA);
	}

	// SOCKS5¥˙¿Ì£¨≤ª…Ë÷√strUserAndPwd ±ª·±¿µƒ£° by ZC. 2010-8-13.
	retval = curl_easy_setopt((CURL*)ulHandle, CURLOPT_PROXYUSERPWD, strUserAndPwd.c_str());
	if (retval != CURLE_OK)
	{
		return (unsigned long)retval;
	} 

	return 0;
}

//////////////////////////////////////////////////////////////////////////
//
// ªÒ»°Http◊¥Ã¨¬Î°£
BOOL KCurlObject::ObtainHttpStatusCode(IN char* pszResult)
{
	BOOL bSuccess = FALSE;

	// ø¥ «≤ª «HTTP◊¥Ã¨∑µªÿ¬Î°£
	if (IsStringValid(pszResult)
		&& !_strnicmp(pszResult, "HTTP/", 5)
		)
	{
		char* pTemp = NULL;

		pTemp = strstr(pszResult, " ");
		if (pTemp && ++pTemp)
		{
			int nSize = strlen(pszResult);
			nSize = nSize - (pTemp - pszResult);

			if (nSize > 6)	nSize = 6;
			
			char szOK[10] = {0};
			memcpy_s(szOK, nSize, pTemp, nSize);

			if (_stricmp(szOK, "200 OK"))
			{
				szOK[3] = '\0';
				this->m_uHttpStatusCode = atoi(szOK);
			}
			else
			{
				m_uHttpStatusCode = 200;
				bSuccess = true;
			}
		}
	} // if !_strnicmp pszResult HTTP/

	return bSuccess;
}